package com.tdkj.dao;

import static org.hibernate.criterion.Example.create;

import java.math.BigInteger;
import java.util.LinkedHashMap;
import java.util.List;

import javax.annotation.Resource;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.stereotype.Component;

import com.tdkj.model.base.BaseEntity;
import com.tdkj.util.Page;


@Component("baseDao")
public class BaseDao implements IBaseDao{
	
	private SessionFactory sessionFactory;
	private final static int EXPORT = -1;
	@Resource
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	private static final Logger log = LoggerFactory.getLogger(BaseDao.class);
	
	public Session getSession(){
		return this.sessionFactory.getCurrentSession();
	}
	
	public void save(BaseEntity bean) {
		String className = bean.getClass().getName();
		
		log.debug("saving "+className+" instance");
		try {
			getSession().save(bean);
			log.debug("save "+className+" successful");
		} catch (HibernateException re) {
			DataAccessException dae = SessionFactoryUtils.convertHibernateAccessException(re);
			log.error("save "+className+" failed", dae);
			throw dae;
		}
	}

	public void delete(BaseEntity bean) {
		String className = bean.getClass().getName();
		
		log.debug("deleting "+className+" instance");
		try {
			getSession().delete(bean);
			log.debug("delete "+className+" successful");
		} catch (HibernateException re) {
			DataAccessException dae = SessionFactoryUtils.convertHibernateAccessException(re);
			log.error("delete "+className+" failed", dae);
			throw dae;
		}
		
	}
	
	@Override
	public void delete(Long id, Class<? extends BaseEntity> clazz) {
		String className = clazz.getName();
		
		log.debug("deleting "+className+" instance");
		try {
			Object bean = getSession().get(clazz, id);
			getSession().delete(bean);
			log.debug("delete "+className+" successful");
		} catch (HibernateException re) {
			DataAccessException dae = SessionFactoryUtils.convertHibernateAccessException(re);
			log.error("delete "+className+" failed", dae);
			throw dae;
		}
	}

	@Override
	public void delete(String propertyName, Object value,
			Class<? extends BaseEntity> clazz) {
		
		String className = clazz.getName();
		
		log.debug("deleting "+className+" instance");
		try {
			String alias = "bean";
			String hql = "delete " + className + " " + alias + " where " + alias+"."+propertyName+"=:" + propertyName;
			//暂用string
			getSession().createQuery(hql).setString(propertyName, String.valueOf(value)).executeUpdate();
			log.debug("delete "+className+" successful");
		} catch (HibernateException re) {
			DataAccessException dae = SessionFactoryUtils.convertHibernateAccessException(re);
			log.error("delete "+className+" failed", dae);
			throw dae;
		}
		
	}

	@SuppressWarnings("unchecked")
	public <T extends BaseEntity> T findById(long id, Class<T> clazz) {
		String className = clazz.getName();
		
		log.debug("getting "+className+" instance with id: " + id);
		try {
			T instance = (T) getSession().get(clazz, id);
			return instance;
		} catch (HibernateException re) {
			DataAccessException dae = SessionFactoryUtils.convertHibernateAccessException(re);
			log.error("get "+className+" failed", dae);
			throw dae;
		}
	}
	
	@Override
	public <T extends BaseEntity> List<T> findByExample(T t) {
		return this.findByExample(t,null);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public <T extends BaseEntity> List<T> findByExample(T t,
			LinkedHashMap<String, String> orderInfos) {
		String className = t.getClass().getName();
		
		log.debug("finding "+className+" instance by example");
		try {
			Criteria criteria = getSession().createCriteria(className).add(create(t));
			
			if(orderInfos!=null && orderInfos.size()!=0){
				for(String propertyName : orderInfos.keySet()){
					if("asc".equalsIgnoreCase(orderInfos.get(propertyName))){
						criteria = criteria.addOrder(Order.asc(propertyName));
					}else{
						criteria = criteria.addOrder(Order.desc(propertyName));
					}
				}
			}
			
			List<T> results = criteria.list();
			log.debug("find by example successful, result size: "
					+ results.size());
			return results;
		} catch (HibernateException re) {
			DataAccessException dae = SessionFactoryUtils.convertHibernateAccessException(re);
			log.error("find by example failed", dae);
			throw dae;
		}
	}

	@Override
	public <T extends BaseEntity> List<T> findByProperty(String propertyName, Object value, Class<T> clazz) {
		return this.findByProperty(propertyName, value, clazz, null);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public <T extends BaseEntity> List<T> findByProperty(String propertyName,
			Object value, Class<T> clazz,
			LinkedHashMap<String, String> orderInfos) {
		
		String className = clazz.getName();
		
		log.debug("finding "+className+" instance with property: " + propertyName
				+ ", value: " + value);
		try {
			String alias = "bean";
			String queryString = "from " + className + " "+ alias + " where " + alias + "." + propertyName + "=? ";
			if(value == null){
				queryString = "from " + className + " "+ alias + " where " + alias + "." + propertyName + " is null ";
			}
			if(orderInfos != null && orderInfos.size() != 0){
				queryString = queryString + " order by ";
				for(String orderPropertyName : orderInfos.keySet()){
					queryString = queryString + " " + alias + "." + orderPropertyName + " " + orderInfos.get(orderPropertyName) + ",";
				}
				queryString = queryString.substring(0, queryString.length()-1);
			}
			
			Query queryObject = getSession().createQuery(queryString);
			if(value != null){
				queryObject.setParameter(0, value);
			}
			return queryObject.list();
		} catch (HibernateException re) {
			DataAccessException dae = SessionFactoryUtils.convertHibernateAccessException(re);
			log.error("find by property name failed", dae);
			throw dae;
		}
		
	}

	@Override
	public <T extends BaseEntity> List<T> findAll(Class<T> clazz) {
		
		return findAll(clazz, null);
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T extends BaseEntity> List<T> findAll(Class<T> clazz,
			LinkedHashMap<String,String> orderInfos) {
		
		String className = clazz.getName();
		
		log.debug("finding all "+className+" instances");
		
		try {
			String alias = "bean";
			String queryString = "from " + className + " "+ alias;
			if(orderInfos != null && orderInfos.size() != 0){
				queryString = queryString + " order by ";
				for(String propertyName : orderInfos.keySet()){
					queryString = queryString + " " + alias + "." + propertyName + " " + orderInfos.get(propertyName) + ",";
				}
				queryString = queryString.substring(0, queryString.length()-1);
			}
			Query queryObject = getSession().createQuery(queryString);
			return queryObject.list();
		} catch (HibernateException re) {
			DataAccessException dae = SessionFactoryUtils.convertHibernateAccessException(re);
			log.error("find all failed", dae);
			throw dae;
		}
	}
	
	
	@SuppressWarnings("unchecked")
	public <T extends BaseEntity> T merge(T bean) {
		String className = bean.getClass().getName();
		
		log.debug("merging "+className+" instance");
		try {
			T result = (T) getSession().merge(bean);
			log.debug("merge "+className+" successful");
			return result;
		} catch (HibernateException re) {
			DataAccessException dae = SessionFactoryUtils.convertHibernateAccessException(re);
			log.error("merge "+className+" failed", dae);
			throw dae;
		}
	}

	public void attachDirty(BaseEntity bean) {
		String className = bean.getClass().getName();
		
		log.debug("attaching dirty "+className+" instance");
		try {
			getSession().saveOrUpdate(bean);
			log.debug("attach "+className+" successful");
		} catch (HibernateException re) {
			DataAccessException dae = SessionFactoryUtils.convertHibernateAccessException(re);
			log.error("attach "+className+" failed", dae);
			throw dae;
		}
	}

	public void attachClean(BaseEntity bean) {
		String className = bean.getClass().getName();
		log.debug("attaching clean "+className+" instance");
		try {
			getSession().lock(bean, LockMode.NONE);
			log.debug("attach "+className+" successful");
		} catch (HibernateException re) {
			DataAccessException dae = SessionFactoryUtils.convertHibernateAccessException(re);
			log.error("attach "+className+" failed", dae);
			throw dae;
		}
	}
	
	@SuppressWarnings("unchecked")
	public boolean isExist(long id, Class<? extends BaseEntity> clazz) {
		String className = clazz.getName();
		
		log.debug("getting "+className+" instance with id: " + id);
		try {
			return getSession().get(clazz, id) != null;
		} catch (HibernateException re) {
			DataAccessException dae = SessionFactoryUtils.convertHibernateAccessException(re);
			log.error("get "+className+" failed", dae);
			throw dae;
		}
	}
	
	
	/**
	 * 分页查询。
	 * 
	 * @param query
	 *            需要分页的查询
	 * @param pageNumber
	 *            当前页码
	 * @param pageSize
	 *            每页显示记录数。
	 */
	@SuppressWarnings("unchecked")
	public Page findBy(String sql, int pageNumber, int pageSize) {
		
		SQLQuery query = this.getSession().createSQLQuery(sql);
		
		String countSql = " select count(*) as C from ("+sql+") R";
		int totalCount = ((BigInteger)this.getSession().createSQLQuery(countSql).uniqueResult()).intValue();

		if (pageNumber < 1)
			pageNumber = 1;
		List list = null;
		int startIndex = 0;
		
		
		if (pageSize == EXPORT) {
			list = query.list();
		} else {
			startIndex = getStartOfAnyPage(pageNumber, pageSize);
			list = query.setFirstResult(startIndex - 1).setMaxResults(pageSize).list();
			// 当前页记录条数
		}
		int avaCount = list == null ? 0 : list.size();
		return new Page(startIndex, avaCount, totalCount, pageSize, list);
	}
	
	/**
	 * 分页查询。
	 * 
	 * @param query
	 *            需要分页的查询
	 * @param pageNumber
	 *            当前页码
	 * @param pageSize
	 *            每页显示记录数。
	 */
	@SuppressWarnings("unchecked")
	public Page findBy(Query query, int pageNumber, int pageSize) {
		// 设置记录集滚动方式以计算总计条数
		ScrollableResults scrollableResults = query.scroll(ScrollMode.SCROLL_SENSITIVE);
		scrollableResults.last();
		// 总计记录条数
		int totalCount = scrollableResults.getRowNumber();

		if (pageNumber < 1)
			pageNumber = 1;
		List list = null;
		int startIndex = 0;
		if (pageSize == EXPORT) {
			list = query.list();
		} else {
			startIndex = getStartOfAnyPage(pageNumber, pageSize);
			list = query.setFirstResult(startIndex - 1).setMaxResults(pageSize).list();
			// 当前页记录条数
		}
		int avaCount = list == null ? 0 : list.size();
		return new Page(startIndex, avaCount, totalCount + 1, pageSize, list);
	}

	/**
	 * 分页查询。
	 * 
	 * @param criteria
	 *            需要分页的查询
	 * @param pageNumber
	 *            当前页码
	 * @param pageSize
	 *            每页显示记录数。
	 */
	@SuppressWarnings("unchecked")
	public Page findBy(Criteria criteria, int pageNumber, int pageSize) {
		// 总计记录条数
		int totalCount = findByOfTotalRows(criteria);

		if (pageNumber < 1)
			pageNumber = 1;

		List list = null;
		int startIndex = 0;
		// 表示需要导出数据,不需要分页.
		if (pageSize == EXPORT) {
			list = criteria.list();
		} else {
			startIndex = Page.getStartOfAnyPage(pageNumber, pageSize);
			list = criteria.setFirstResult(startIndex - 1).setMaxResults(pageSize).list();
		}
		// 当前页记录条数
		int avaCount = list == null ? 0 : list.size();

		return new Page(startIndex, avaCount, totalCount + 1, pageSize, list);
	}

	/**
	 * 分页查询。
	 * 
	 * @param criteria
	 *            需要分页的查询
	 * @param pageNumber
	 *            当前页码
	 * @param pageSize
	 *            每页显示记录数。
	 */
	@SuppressWarnings("unchecked")
	public Page findPageBy(Criteria criteria, int pageNumber, int pageSize) {
		if (pageNumber < 1)
			pageNumber = 1;
		// 总计记录条数
		int totalCount = findPageByOfTotalRows(criteria);

		List list = null;
		int startIndex = 0;
		// 表示需要导出数据,不需要分页.
		if (pageSize == EXPORT) {
			list = criteria.list();
		} else {
			startIndex = getStartOfAnyPage(pageNumber, pageSize);
			log.debug("***********findBy**************");
			
			//由数据库分页SQL实现
			list = criteria.setFirstResult(startIndex - 1).setMaxResults(pageSize).list();
		}
		// 当前页记录条数
		int avaCount = list == null ? 0 : list.size();

		log.debug("总计记录条数: " + totalCount);

		return new Page(startIndex, avaCount, totalCount + 1, pageSize, list);
	}

	/**
	 * 支持可滚动结果集
	 * 如果数据量大,很影响性能
	 * @param criteria
	 * @return
	 */
	protected int findByOfTotalRows(Criteria criteria) {
		// 设置记录集滚动方式以计算总计条数
		ScrollableResults scrollableResults = criteria.scroll(ScrollMode.SCROLL_SENSITIVE);
		scrollableResults.last();
//		
		// 总计记录条数
		return scrollableResults.getRowNumber();
		
//		return  findPageByOfTotalRows(criteria);
	}

	protected int findByOfTotalRows(Query query) {
		// 设置记录集滚动方式以计算总计条数
		ScrollableResults scrollableResults = query.scroll(ScrollMode.SCROLL_SENSITIVE);
		scrollableResults.last();
		// 总计记录条数
		return scrollableResults.getRowNumber();
	}

	/**
	 * 分页查询时获取总的记录条数
	 * @param criteria
	 * @return
	 */
	protected int findPageByOfTotalRows(Criteria criteria) {
		int totalCount = ((Integer) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();   
		criteria.setProjection(null);
		criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY); 
		return totalCount;
	}

	/**
	 * 获取任一页第一条数据在数据库中的位置
	 */
	public static int getStartOfAnyPage(int pageNo, int pageSize) {
		int startIndex = (pageNo - 1) * pageSize + 1;
		if (startIndex < 1)
			startIndex = 1;
		return startIndex;
	}

	@Override
	public <T extends BaseEntity> List<T> findAll(Class<T> clazz,
			String orderByPropertyName, String orderByType) {
		LinkedHashMap<String, String> orderInfos = new LinkedHashMap<String, String>();
		orderInfos.put(orderByPropertyName, orderByType);
		return this.findAll(clazz, orderInfos);
	}

	@Override
	public <T extends BaseEntity> List<T> findByExample(T t,
			String orderByPropertyName, String orderByType) {
		LinkedHashMap<String, String> orderInfos = new LinkedHashMap<String, String>();
		orderInfos.put(orderByPropertyName, orderByType);
		return this.findByExample(t, orderInfos);
	}

	@Override
	public <T extends BaseEntity> List<T> findByProperty(String propertyName,
			Object value, Class<T> clazz, String orderByPropertyName,
			String orderByType) {
		LinkedHashMap<String, String> orderInfos = new LinkedHashMap<String, String>();
		orderInfos.put(orderByPropertyName, orderByType);
		return this.findByProperty(propertyName, value, clazz, orderInfos);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Object> findBySql(String sql) {
		return this.getSession().createSQLQuery(sql).list();
	}

}
